<!doctype html>
<html>
    <head>
        <title>HT for Web 序列化手册</title>
        <meta charset="UTF-8">    
        <meta name="viewport" content="user-scalable=yes, width=1024">
        <style type="text/css"> 
            h1, h2, h3, h4, h5, h6, p, blockquote {
                margin: 0;
                padding: 0;
            }
            body {
                font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif;
                font-size: 13px;
                line-height: 18px;
                color: #737373;
                background-color: white; 
                margin: 10px 13px 10px 13px;
            }
            table {
                margin: 10px 0 15px 0;
                border-collapse: collapse;
            }
            td,th {	
                border: 1px solid #ddd;
                padding: 3px 10px;
            }
            th {
                padding: 5px 10px;	
            }
            a {
                color: #0069d6;
            }
            a:hover {
                color: #0050a3;
                text-decoration: none;
            }
            a img {
                border: none;
            }
            p {
                margin-bottom: 9px;
            }
            h1, h2, h3, h4, h5, h6 {
                color: #404040;
                line-height: 36px;
            }
            h1 {
                margin-bottom: 18px;
                font-size: 30px;
            }
            h2 {
                font-size: 24px;
            }
            h3 {
                font-size: 18px;
            }
            h4 {
                font-size: 16px;
            }
            h5 {
                font-size: 14px;
            }
            h6 {
                font-size: 13px;
            }
            hr {
                margin: 0 0 19px;
                border: 0;
                border-bottom: 1px solid #ccc;
            }
            blockquote {
                padding: 13px 13px 21px 15px;
                margin-bottom: 18px;
                font-family:georgia,serif;
                font-style: italic;
            }
            blockquote:before {
                content:"\201C";
                font-size:40px;
                margin-left:-10px;
                font-family:georgia,serif;
                color:#eee;
            }
            blockquote p {
                font-size: 14px;
                font-weight: 300;
                line-height: 18px;
                margin-bottom: 0;
                font-style: italic;
            }
            code, pre {
                font-family: Monaco, Andale Mono, Courier New, monospace;
            }
            code {
                background-color: #fee9cc;
                color: rgba(0, 0, 0, 0.75);
                padding: 1px 3px;
                font-size: 12px;
                -webkit-border-radius: 3px;
                -moz-border-radius: 3px;
                border-radius: 3px;
            }
            pre {
                display: block;
                padding: 14px;
                margin: 0 0 18px;
                line-height: 16px;
                font-size: 11px;
                border: 1px solid #d9d9d9;
                white-space: pre-wrap;
                word-wrap: break-word;
            }
            pre code {
                background-color: #fff;
                color:#737373;
                font-size: 11px;
                padding: 0;
            }
            sup {
                font-size: 0.83em;
                vertical-align: super;
                line-height: 0;
            }
            * {
                -webkit-print-color-adjust: exact;
            }
            @media screen and (min-width: 914px) {
                body {
                    width: 854px;
                    margin:10px auto;
                }
            }
            @media print {
                body,code,pre code,h1,h2,h3,h4,h5,h6 {
                    color: black;
                }
                table, pre {
                    page-break-inside: avoid;
                }
            }                        
            iframe{
                width: 100%;
                border: 1px solid #34495E;
                margin: 0;                
            }            
            .logo{
                vertical-align: middle;
            }

        </style>        
        <script>
            function init() {
                var logoSrc = '',
                    logos = document.querySelectorAll('.logo'),
                    i = 0;
                for (; i < logos.length; i++) {
                    logos[i].src = logoSrc;
                }
                
                var iframes = document.querySelectorAll('iframe'),
                    func = function(){};
                for (i=0; i < iframes.length; i++) {
                    var iframe = iframes[i];
                    
                    // a small hack to make it work on android
                    iframe.ontouchstart = func;
                    
                    var div = document.createElement('div');
                    div.innerHTML = '<a href="' + iframe.src + '" target="_blank">独立运行→</a>';
                    iframe.parentNode.insertBefore(div, iframe);
                }   
            }
        </script>    
    </head>
    <body onload="init();">
        <a href="http://www.hightopo.com"><img class="logo"></a>HT for Web 序列化手册
        <hr style="margin: 1px 0 20px 0">
<p>索引</p>

<ul><li><a href="#ref_overview">概述</a></li><li><a href="#ref_classbasic">类基础</a>    </li><li><a href="#ref_serialization">序列化</a></li></ul>

<hr/>

<div id="ref_overview"></div>

<h2>概述</h2>

<p><code>HT for Web</code>对数据模型<code>ht.DataModel</code>提供了<code>ht.JSONSerializer</code>的序列化功能，
可将<code>DataModel</code>中包含的<code>Data</code>对象以及属性序列化为<a href="http://en.wikipedia.org/wiki/JSON">JSON</a>格式，
同时提供反序列化构建<code>DataModel</code>数据对象的功能。本手册将详细介绍<code>HT for Web</code>的基本类定义规则，<code>ht.JSONSerializer</code>类的基本使用，
以及自定义类和属性的序列化和反序列化案例。</p>

<p><iframe src="examples/example_exportimport.html" style="height:400px"></iframe></p>

<div id="ref_classbasic"></div>

<h2>类基础</h2>

<p><code>JavaScript</code>语言未提供严谨的面对对象类的语言级支持，为此<code>HT</code>设计了一套类封装体系供用户选择采用。</p>

<p>定义和使用<code>com.hightopo.Person</code>类的步骤如下：</p>

<p>1、定义包目录结构</p>

<pre><code>// define package
com = {};
com.hightopo = {};</code></pre>

<p>2、定义类构造函数</p>

<pre><code>// define Person class constructor
com.hightopo.Person = function(firstName, lastName){    
    com.hightopo.Person.superClass.constructor.call(this);
    this.setFirstName(firstName);
    this.setLastName(lastName);
};</code></pre>

<p>3、定义类属性和函数</p>

<pre><code>// define Person class properties and methods
ht.Default.def(&#39;com.hightopo.Person&#39;, Object, {
    _firstName: null,
    _lastName: null,
    getFirstName: function(){
        return this._firstName;
    },
    setFirstName: function(firstName){
        this._firstName = firstName;
    },
    getLastName: function(){
        return this._lastName;
    },
    setLastName: function(lastName){
        this._lastName = lastName;
    },
    getDescription: function(){
        return &#39;firstName:&#39; + this._firstName + &#39; lastName:&#39; + this._lastName;
    }
});</code></pre>

<p>4、构建对象和函数调用</p>

<pre><code>var person = new com.hightopo.Person(&#39;eric&#39;, &#39;lin&#39;);
console.log(&#39;FirstName - &#39; + person.getFirstName());
console.log(&#39;LastName - &#39; + person.getLastName());
console.log(&#39;Description - &#39; + person.getDescription());</code></pre>

<p>5、输出结果</p>

<pre><code>FirstName - eric 
LastName - lin 
Description - firstName:eric lastName:lin </code></pre>

<p>定义和使用<code>com.hightopo.Person</code>的子类<code>com.hightopo.Student</code>的步骤如下：</p>

<p>1、定义类构造函数</p>

<pre><code>// define Student class constructor
com.hightopo.Student = function(firstName, lastName, grade){
    com.hightopo.Student.superClass.constructor.call(this, firstName, lastName);
    this.setGrade(grade);
};</code></pre>

<p>2、定义类属性和函数</p>

<pre><code>// define Student class properties and methods
ht.Default.def(&#39;com.hightopo.Student&#39;, com.hightopo.Person, {
    _grade: null,
    getGrade: function(){
        return this._grade;
    },
    setGrade: function(grade){
        this._grade = grade;
    },
    // override getDescription method                                
    getDescription: function(){
        var desc = com.hightopo.Student.superClass.getDescription.call(this);
        return desc + &#39; grade:&#39; + this._grade;
    }        
});</code></pre>

<p>3、构建对象和函数调用</p>

<pre><code>var student = new com.hightopo.Student(&#39;ben&#39;, &#39;lin&#39;, 2);
console.log(&#39;FirstName - &#39; + student.getFirstName());
console.log(&#39;LastName - &#39; + student.getLastName());
console.log(&#39;Grade - &#39; + student.getGrade());
console.log(&#39;Description - &#39; + student.getDescription());</code></pre>

<p>4、输出结果</p>

<pre><code>FirstName - ben 
LastName - lin 
Grade - 2 
Description - firstName:ben lastName:lin grade:2 </code></pre>

<div id="ref_serialization"></div>

<h2>序列化</h2>

<p><code>ht.JSONSerializer</code>可对<code>DataModel</code>数据模型的<code>Data</code>数据对象以及<code>DataModel</code>自身的<code>attr</code>属性，
进行<code>JSON</code>格式的序列化和反序列化，默认会<code>HT</code>所有预定义类的<code>get/set</code>属性，<code>style</code>和<code>attr</code>属性进行序列化，
<code>JSONSerializer</code>类的函数如下：</p>

<ul><li><code>toJSON()</code>函数返回序列化的<code>JSON</code>对象</li><li><code>serialize(space)</code>返回序列化后<code>JSON</code>对象的字符串内容，<code>space</code>为空格缩进值，默认为<code>2</code></li><li><code>deserialize(json, rootParent, setId)</code>反序列化，并将反序列化的对象加入<code>DataModel</code><ul><li><code>json</code>：此参数可为<code>JSON</code>对象<code>Object</code>，也可为字符串化的<code>JSON</code>对象</li><li><code>rootParent</code>：如果反序列化的<code>Data</code>对象的<code>parent</code>为空，则调用<code>data.setParent(rootParent)</code>为其设置<code>parent</code></li><li><code>setId</code>：指定是否用<code>JSON</code>的<code>i</code>信息作为<code>Data</code>的<code>id</code>属性，默认为<code>false</code>由<code>Data</code>构造函数自动生成<code>id</code>    </li></ul></li><li><code>isSerializable(data)</code>函数默认返回<code>true</code>代表序列化所有对象，重载该函数可自定义<code>data</code>是否参与序列化</li><li><code>getProperties(data)</code>函数返回<code>Object</code>类型的<code>map</code>对象，<code>key</code>为要序列化的<code>get/set</code>属性</li><li><code>getStyles(data)</code>函数返回<code>Object</code>类型的<code>map</code>对象，<code>key</code>为要序列化的<code>style</code>属性</li><li><code>getAttrs(data)</code>函数返回<code>Object</code>类型的<code>map</code>对象，<code>key</code>为要序列化的<code>attr</code>属性</li></ul>

<blockquote><p>字符串化的<code>JSON</code>参数常出现因换行信息导致解析出错，对于这种情况可通过<code>json.replace(/\n/g,&quot;&quot;)</code>对字符串参数做预处理</p></blockquote>

<p>以下为<code>get/set</code>、<code>style</code>和<code>attr</code>属性是否序列化相关函数的默认实现逻辑，可重载自定义，默认实现皆调用了<code>Data</code>对象的相应函数，
因此也可以通过重载<code>Data</code>对象类型的实现改变逻辑。</p>

<p><code>ht.JSONSerializer</code>默认逻辑：</p>

<pre><code>getProperties: function(data){        
    return data.getSerializableProperties();
},
getStyles: function(data){
    return data.getSerializableStyles();
},
getAttrs: function(data){
    return data.getSerializableAttrs();
},</code></pre>

<p><code>ht.Data</code>默认逻辑：</p>

<pre><code>getSerializableProperties: function(){
    return {
        name: 1,
        displayName: 1, 
        icon: 1, 
        toolTip: 1, 
        parent: 1,
        layer: 1,
        tag: 1,
        adjustChildrenToTop: 1            
    };
},
getSerializableStyles: function(){            
    var name, map = {};
    for (name in this._styleMap) {            
        map[name] = 1;
    }
    return map; 
},
getSerializableAttrs: function(){
    var name, map = {};
    for (name in this._attrObject) {            
        map[name] = 1;
    }
    return map; 
}</code></pre>

<p><code>ht.Edge</code>默认逻辑：</p>

<pre><code>getSerializableProperties: function(){
    var map = ht.Edge.superClass.getSerializableProperties.call(this);        
    addMethod(map, {
        source: 1,
        target: 1
    });                
    return map;
}</code></pre>

<p>以下代码为<code>DataModel</code>类封装的便捷函数，如果不需要自定义序列化逻辑，直接操作<code>DataModel</code>的序列化相关函数即可。</p>

<pre><code>serialize: function(space){
    return (new ht.JSONSerializer(this)).serialize(space);
},
toJSON: function(){       
    return (new ht.JSONSerializer(this)).toJSON();
},
deserialize: function (json, rootParent, setId) {
    (new ht.JSONSerializer(this)).deserialize(json, rootParent, setId);
} </code></pre>

<p>以下例子构建了<code>MyData</code>的继承于<code>ht.Data</code>的子类，重载了<code>getSerializableProperties</code>函数，增加了对<code>age</code>属性的存储，
重载了<code>getSerializableAttrs</code>函数，仅对<code>attr2</code>属性进行存储，通过<code>var jsonString = dataModel.serialize();</code>序列化
和<code>dataModel.deserialize(jsonString)</code>反序列化之后，<code>attr1</code>丢失外，其他属性皆恢复原始值。</p>

<p>例子代码如下：</p>

<pre><code>// define package
com = {};
com.hightopo = {};

// define MyData
var MyData = com.hightopo.MyData = function(age, attr1, attr2){    
    MyData.superClass.constructor.call(this);
    this.setAge(age);
    this.setAttr(&#39;attr1&#39;, attr1);
    this.setAttr(&#39;attr2&#39;, attr2);
};
ht.Default.def(&#39;com.hightopo.MyData&#39;, ht.Data, {
    _age: 1,
    getAge: function(){
        return this._age;
    },
    setAge: function(age){
        if(age !== this._age){
            var oldValue = this._age;
            this._age = age;
            this.firePropertyChange(&#39;age&#39;, oldValue, age);
        }
    },
    getSerializableProperties: function(){
        var map = MyData.superClass.getSerializableProperties.call(this);        
        map.age = true;              
        return map;
    },
    getSerializableAttrs: function(){
        return {
            attr2: 1
        }; 
    }                         
});                

var dataModel = new ht.DataModel();
dataModel.setAttr(&#39;website&#39;, &#39;www.hightopo.com&#39;);                

var data = new MyData(34, &#39;tw&#39;, &#39;ht&#39;);
data.setTag(&#39;20130301&#39;);
dataModel.add(data);                                                  

var jsonString = dataModel.serialize();
console.log(jsonString); 

dataModel.clear();
dataModel.setAttr(&#39;website&#39;, null);
dataModel.deserialize(jsonString);
data = dataModel.getDataByTag(&#39;20130301&#39;);

console.log(data.getAge());
console.log(data.getAttr(&#39;attr1&#39;));
console.log(data.getAttr(&#39;attr2&#39;));
console.log(dataModel.getAttr(&#39;website&#39;));</code></pre>

<p>运行结果如下：</p>

<pre><code>{
  &quot;v&quot;: &quot;2.8&quot;,
  &quot;d&quot;: [
    {
      &quot;c&quot;: &quot;com.hightopo.MyData&quot;,
      &quot;i&quot;: 3,
      &quot;p&quot;: {
        &quot;tag&quot;: &quot;20130301&quot;,
        &quot;age&quot;: 34
      },
      &quot;a&quot;: {
        &quot;attr2&quot;: &quot;ht&quot;
      }
    }
  ],
  &quot;a&quot;: {
    &quot;website&quot;: &quot;www.hightopo.com&quot;
  }
} 

34 
undefined 
ht 
www.hightopo.com </code></pre>    <hr id="contact" style="margin: 20px 0 1px 0">
    <a href="http://www.hightopo.com"><img class="logo"></a>欢迎交流 <a href="mailto:service@hightopo.com">service@hightopo.com</a>
</body>
</html>
